﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using Elca.MvvmHelpers;
using Microsoft.Win32;

namespace ULSDeobfuscator {

    public class LogFileChooserViewModel : BaseViewModel, IDropTarget {

        // always true except during drag&drop
        private bool _shouldRecomputeSelectedFiles = true;

        private readonly Dispatcher _uiDispatcher = Dispatcher.CurrentDispatcher;

        private static DateTime s_lastSelectedLogDate = DateTime.MinValue;
        public DateTime UserSelectedDate { get; set; }

        private bool _isFileFromDirectorySelected;
        public bool IsFileFromDirectorySelected {
            get { return _isFileFromDirectorySelected; }
            set {
                if (_isFileFromDirectorySelected != value) {
                    _isFileFromDirectorySelected = value;
                    OnPropertyChanged("IsFileFromDirectorySelected");

                    if (_isFileFromDirectorySelected) {
                        RecomputeSelectedLogFiles();
                    }
                }
            }
        }

        private bool _isDateTimeSelected;
        public bool IsDateTimeSelected {
            get { return _isDateTimeSelected; }
            set {
                if (_isDateTimeSelected != value) {
                    _isDateTimeSelected = value;
                    OnPropertyChanged("IsDateTimeSelected");

                    if (_isDateTimeSelected) {
                        RecomputeSelectedLogFiles();
                    }
                }
            }
        }

        private bool _isExplicitFileSelected;
        public bool IsExplicitFileSelected {
            get { return _isExplicitFileSelected; }
            set {
                if (_isExplicitFileSelected != value) {
                    _isExplicitFileSelected = value;
                    OnPropertyChanged("IsExplicitFileSelected");

                    if (_isExplicitFileSelected) {
                        RecomputeSelectedLogFiles();
                    }
                }
            }
        }

        private bool _isRecentLogFileSelected;
        public bool IsRecentLogFileSelected {
            get { return _isRecentLogFileSelected; }
            set {
                if (_isRecentLogFileSelected != value) {
                    _isRecentLogFileSelected = value;
                    OnPropertyChanged("IsRecentLogFileSelected");

                    if (_isRecentLogFileSelected) {
                        RecomputeSelectedLogFiles();
                    }
                }
            }
        }

        private string _sharePointLogsPath;

        public ICommand LoadCommand { get; private set; }
        public ICommand UseSharePointLogsDirectoryCommand { get; private set; }
        public ICommand CancelCommand { get; private set; }
        public ICommand OpenCommand { get; private set; }
        public ICommand BrowseForExplicitLogFileCommand { get; private set; }
        public ICommand BrowseForLogsDirectory { get; private set; }
        public ICommand RefreshAnalysisTimeCommand { get; private set; }
        
        public string SharePointLogsDirectory {
            get {
                return _sharePointLogsPath ?? "(No SharePoint found on this PC)";
            }
        }

        private string _currentLogsPath;
        public string CurrentLogsPath {
            get { return _currentLogsPath; }
            set {
                if (_currentLogsPath != value) {
                    _currentLogsPath = value;
                    OnPropertyChanged("CurrentLogsPath");

                    ReadListOfAvailableLogFiles();
                }
            }
        }

        private string _explicitLogFilePath;
        public string ExplicitLogFilePath {
            get { return _explicitLogFilePath; }
            set {
                if (_explicitLogFilePath != value) {
                    _explicitLogFilePath = value;
                    OnPropertyChanged("ExplicitLogFilePath");
                    IsExplicitFileSelected = true;
                    RecomputeSelectedLogFiles();
                }
            }
        }

        public ObservableCollection<LogFileInfo> LogFiles { get; private set; }
        public ObservableCollection<LogFileInfo> RecentLogFiles { get; private set; }

        public ObservableCollection<string> RecentDirectories { get; private set; }
        // only useful during a session. Enables staying on the same dir for several displays of this dialog
        private static string s_lastDirectory;

        public bool IsRecentLogFilesSelectable {
            get { return RecentLogFiles.Count > 0; }
        }

        private LogFileInfo _selectedLogFileByDateTime;

        private List<LogFileInfo> _selectedLogFiles = new List<LogFileInfo>();
        public List<LogFileInfo> SelectedLogFiles {
            get { return _selectedLogFiles; }
            set {
                if (_selectedLogFiles != value) {
                    _selectedLogFiles = value;
                    OnPropertyChanged("SelectedLogFiles");
                }
            }
        }

        private ObservableCollection<LogFileInfo> _selectedLogFilesFromDirectory = new ObservableCollection<LogFileInfo>();
        public ObservableCollection<LogFileInfo> SelectedLogFilesFromDirectory {
            get { return _selectedLogFilesFromDirectory; }
            set {
                if (_selectedLogFilesFromDirectory != value) {
                    _selectedLogFilesFromDirectory = value;
                    OnPropertyChanged("SelectedLogFilesFromDirectory");
                }
            }
        }

        private ObservableCollection<LogFileInfo> _recentSelectedLogFiles = new ObservableCollection<LogFileInfo>();
        public ObservableCollection<LogFileInfo> RecentSelectedLogFiles {
            get { return _recentSelectedLogFiles; }
            set {
                if (_recentSelectedLogFiles != value) {
                    _recentSelectedLogFiles = value;
                    OnPropertyChanged("RecentSelectedLogFiles");
                    IsRecentLogFileSelected = true;
                }
            }
        }

        private string _selectedLogDate;
        /// <summary>
        /// Date chosen by user; we'll try to find a log file that is close to this date
        /// </summary>
        public string SelectedLogDate { 
            get { return _selectedLogDate; }
            set {
                if (_selectedLogDate != value) {
                    _selectedLogDate = value;

                    IsDateTimeSelected = true;

                    OnPropertyChanged("SelectedLogDate");
                    ComputeClosestDateExplanation();
                }
            }
        }

        private string _closestLogFileExplanation;
        public string ClosestLogFileExplanation {
            get { return _closestLogFileExplanation; }
        }

        // no need to use OnPropertyChanged. This is set outside this class and does not change
        public Visibility ChooseAdditionOrReplaceVisibility { get; set; }

        // no need for OnPropertyChanged. This is set once and for all
        public bool IsWindowVisibleInTaskbar { get; set; }

        private bool _isLogAddition;
        public bool IsLogAddition {
            get { return _isLogAddition; }
            set {
                if (_isLogAddition != value) {
                    _isLogAddition = value;
                    OnPropertyChanged("IsLogAddition");
                }
            }
        }

        private bool _isLogReplacement;
        public bool IsLogReplacement {
            get { return _isLogReplacement; }
            set {
                if (_isLogReplacement != value) {
                    _isLogReplacement = value;
                    OnPropertyChanged("IsLogReplacement");
                }
            }
        }

        public bool IsDuplicateRemovalEnabled {
            get { return App.GlobalSettings.IsDuplicateRemovalEnabled; }
            set {
                if ( App.GlobalSettings.IsDuplicateRemovalEnabled != value) {
                     App.GlobalSettings.IsDuplicateRemovalEnabled = value;
                    OnPropertyChanged("IsLogReplacement");
                }
            }
        }

        public double WindowHeight {
            get { return App.GlobalSettings.ChooserWindowHeight; }
            set {
                if (App.GlobalSettings.ChooserWindowHeight != value) {
                    App.GlobalSettings.ChooserWindowHeight = value;
                    OnPropertyChanged("WindowHeight");
                }
            }
        }

        public double WindowWidth {
            get { return App.GlobalSettings.ChooserWindowWidth; }
            set {
                if (App.GlobalSettings.ChooserWindowWidth != value) {
                    App.GlobalSettings.ChooserWindowWidth = value;
                    OnPropertyChanged("WindowWidth");
                }
            }
        }

        public double WindowLeft {
            get { return App.GlobalSettings.ChooserWindowLeft; }
            set {
                if (App.GlobalSettings.ChooserWindowLeft != value) {
                    App.GlobalSettings.ChooserWindowLeft = value;
                    OnPropertyChanged("WindowLeft");
                }
            }
        }

        public double WindowTop {
            get { return App.GlobalSettings.ChooserWindowTop; }
            set {
                if (App.GlobalSettings.ChooserWindowTop != value) {
                    App.GlobalSettings.ChooserWindowTop = value;
                    OnPropertyChanged("WindowTop");
                }
            }
        }

        public double HostNameWidth {
            get { return App.GlobalSettings.ChooserHostNameWidth; }
            set {
                if (App.GlobalSettings.ChooserHostNameWidth != value) {
                    App.GlobalSettings.ChooserHostNameWidth = value;
                    OnPropertyChanged("HostNameWidth");
                }
            }
        }

        public double DayWidth {
            get { return App.GlobalSettings.ChooserDayWidth; }
            set {
                if (App.GlobalSettings.ChooserDayWidth != value) {
                    App.GlobalSettings.ChooserDayWidth = value;
                    OnPropertyChanged("DayWidth");
                }
            }
        }

        public double TimeWidth {
            get { return App.GlobalSettings.ChooserTimeWidth; }
            set {
                if (App.GlobalSettings.ChooserTimeWidth != value) {
                    App.GlobalSettings.ChooserTimeWidth = value;
                    OnPropertyChanged("TimeWidth");
                }
            }
        }

        public double SizeWidth {
            get { return App.GlobalSettings.ChooserSizeWidth; }
            set {
                if (App.GlobalSettings.ChooserSizeWidth != value) {
                    App.GlobalSettings.ChooserSizeWidth = value;
                    OnPropertyChanged("SizeWidth");
                }
            }
        }

        public double OriginalFileWidth {
            get { return App.GlobalSettings.ChooserOriginalFileWidth; }
            set {
                if (App.GlobalSettings.ChooserOriginalFileWidth != value) {
                    App.GlobalSettings.ChooserOriginalFileWidth = value;
                    OnPropertyChanged("OriginalFileWidth");
                }
            }
        }

        public string Version => $"ULS Deobfuscator version {App.VersionNumber}";

        public LogFileChooserViewModel() {

            DebugLog("Started LogFileChooserViewModel");

            UseSharePointLogsDirectoryCommand = new ConditionalCommand(OnUseSharePointLogsDirectory, () => _sharePointLogsPath != null && _currentLogsPath != _sharePointLogsPath);
            CancelCommand = new ExecutableCommand(OnCancel);
            OpenCommand = new ConditionalCommand(OnOpen, () => SelectedLogFiles.Count > 0);
            BrowseForExplicitLogFileCommand = new ExecutableCommand(OnBrowseForExplicitLogFile);
            BrowseForLogsDirectory = new ExecutableCommand(OnBrowseForLogsDirectory);
            RefreshAnalysisTimeCommand = new ExecutableCommand(OnRefreshAnalysisTime);
            LoadCommand = new ExecutableCommand(OnLoad);
            _selectedLogFilesFromDirectory.CollectionChanged += OnSelectedFileFromDirectoryChanged;
            _recentSelectedLogFiles.CollectionChanged += OnRecentSelectedLogFilesChanged;

            RecentDirectories = new ObservableCollection<string>();
            foreach (string s in App.GlobalSettings.RecentlyOpenedDirectories) {
                DirectoryInfo di = new DirectoryInfo(s);
                if (di.Exists) {
                    RecentDirectories.Add(s);
                }
            }
            DetermineLogDirectory();
            AddDirectoryToRecentList(_sharePointLogsPath);

            LogFiles = new ObservableCollection<LogFileInfo>();
            RecentLogFiles = new ObservableCollection<LogFileInfo>();
            foreach (string s in App.GlobalSettings.RecentlyOpenedFiles) {
                FileInfo fi = new FileInfo(s);
                if (fi.Exists) {
                    RecentLogFiles.Add(new LogFileInfo(fi));
                }
            }

            DateTime dt;
            if (s_lastSelectedLogDate != DateTime.MinValue) {
                dt = s_lastSelectedLogDate;
            } else {
                dt = DateTime.Now;
            }
            _selectedLogDate = ToLogDate(dt);

            SetupFileSystemWatcher();

            DebugLog("Leaving LogFileChooserViewModel");
        }

        private static string ToLogDate(DateTime dt) {
            return dt.ToShortDateString() + " " + dt.ToShortTimeString();
        }

        private void OnRefreshAnalysisTime() {
            SelectedLogDate = ToLogDate(DateTime.Now);
        }

        private void OnRecentSelectedLogFilesChanged(object sender, NotifyCollectionChangedEventArgs e) {
            
            if (IsRecentLogFileSelected) {
                RecomputeSelectedLogFiles();
            } else {
                IsRecentLogFileSelected = true;
            }
        }

        private void OnSelectedFileFromDirectoryChanged(object sender, NotifyCollectionChangedEventArgs e) {

            if (IsFileFromDirectorySelected) {
                RecomputeSelectedLogFiles();
            } else {
                IsFileFromDirectorySelected = true;
            }
        }

        private void OnLoad() {

            DebugLog("In LogFileChooserViewModel.OnLoad");

            // in order to have the binding between the following properties and the check box IsChecked property,
            // a change must occur; this will trigger the attached property code
            IsDateTimeSelected = true;
            IsRecentLogFileSelected = true;
            IsExplicitFileSelected = true;
            IsFileFromDirectorySelected = true;

            // same here
            IsLogReplacement = true;
            IsLogAddition = true;

            if (String.IsNullOrEmpty(s_lastDirectory)) {
                OnUseSharePointLogsDirectory();
            } else {
                CurrentLogsPath = s_lastDirectory;
            }

            DebugLog("Leaving LogFileChooserViewModel.OnLoad");
        }

        private void RecomputeSelectedLogFiles() {
            
            SelectedLogFiles.Clear();

            if (IsFileFromDirectorySelected) {

                SelectedLogFiles.AddRange(SelectedLogFilesFromDirectory);
            } else if (IsDateTimeSelected) {

                if (_selectedLogFileByDateTime != null) {
                    SelectedLogFiles.Add(_selectedLogFileByDateTime);
                }
            } else if (IsRecentLogFileSelected) {

                SelectedLogFiles.AddRange(RecentSelectedLogFiles);
            } else if (IsExplicitFileSelected && _explicitLogFilePath != null) {

                FileInfo fi = new FileInfo(_explicitLogFilePath);
                if (fi.Exists) {
                    SelectedLogFiles.Add(new LogFileInfo(fi));
                } 
            } // else keep the SelectedLogFiles collection empty
        }

        private void OnBrowseForExplicitLogFile() {

            IBrowseFileSystem bfs = ServiceRegistry.Resolve<IBrowseFileSystem>();

            string newLogsFile;
            if (bfs.AskForFileToOpen("Select a log file to display", "Log file", "log", _explicitLogFilePath, out newLogsFile)) {

                ExplicitLogFilePath = newLogsFile;
            }
        }

        private void OnBrowseForLogsDirectory() {

            IBrowseFileSystem bfs = ServiceRegistry.Resolve<IBrowseFileSystem>();

            string newPath;
            if (bfs.AskForDirectory("Select a directory containing log files", _currentLogsPath, out newPath)) {

                CurrentLogsPath = newPath;
            }
        }

        // called when closing, either with OK or cancel results
        private void OnClosing() {

            RememberDirectories();
            TearDownFileSystemWatcher();
        }

        private void OnCancel() {

            OnClosing();
            RaiseCloseRequest(false);
        }

        private void OnOpen() {

            // always true except during drag&drop
            if (_shouldRecomputeSelectedFiles) {
                RecomputeSelectedLogFiles();
            }

            if (OpenCommand.CanExecute(null)) {

                foreach (LogFileInfo lfi in SelectedLogFiles) {

                    string filePath = lfi.OriginalFile.FullName;
                    if (! App.GlobalSettings.RecentlyOpenedFiles.Contains(filePath)) {

                        // do not keep more than 10 files
                        if (App.GlobalSettings.RecentlyOpenedFiles.Count >= 10) {
                            App.GlobalSettings.RecentlyOpenedFiles.RemoveAt(0); // how to remove the 'best' one ???
                        }
                        App.GlobalSettings.RecentlyOpenedFiles.Add(filePath);
                    }
                }

                OnClosing();

                if (! IsDateTimeSelected) {
                    UserSelectedDate = DateTime.MinValue; // tell our caller that the user did not choose a date
                }
                RaiseCloseRequest(true);
            }
        }

        private void RememberDirectories() {

            s_lastDirectory = CurrentLogsPath;

            foreach (string path in RecentDirectories) {
                if (! App.GlobalSettings.RecentlyOpenedDirectories.Contains(path)) {
                    // do not keep more than 10 files
                    if (App.GlobalSettings.RecentlyOpenedDirectories.Count >= 10) {
                        App.GlobalSettings.RecentlyOpenedDirectories.RemoveAt(0); // how to remove the 'best' one ???
                    }
                    App.GlobalSettings.RecentlyOpenedDirectories.Add(path);
                }
            }
        }

        private void OnUseSharePointLogsDirectory() {
            if (_sharePointLogsPath != null) {
                CurrentLogsPath = _sharePointLogsPath;
            }
        }

        private void AddDirectoryToRecentList(string path) {

            if (! String.IsNullOrEmpty(path)) {
                
                if (! RecentDirectories.Contains(path)) {
                    RecentDirectories.Add(path);
                }
            }
        }

        private readonly FileSystemWatcher _dirWatcher = new FileSystemWatcher();
        public const string LogPattern = "*.log";
        private const string ToAvoidFileNameStart = "PSCDiag";

        private void ReadListOfAvailableLogFiles() {

            if (_currentLogsPath != null) {

                DirectoryInfo currentLogsDir = new DirectoryInfo(_currentLogsPath);

                if (currentLogsDir.Exists) {
                    FileInfo[] files = currentLogsDir.GetFiles(LogPattern);

                    LogFiles.Clear();
                    IEnumerable<LogFileInfo> tempLogs =
                            files.Where(fi => ! fi.Name.StartsWith(ToAvoidFileNameStart)).Select(fi => new LogFileInfo(fi));
                    foreach (LogFileInfo lfi in tempLogs) {

                        // if something happens in the LogFileInfo constructor, the OriginalFile property is set to null
                        if (lfi.OriginalFile != null) {
                            LogFiles.Add(lfi);
                        }
                    }

                    AddDirectoryToRecentList(_currentLogsPath);

                    ConfigureFileSystemWatcher();

                    ComputeClosestDateExplanation();
                }
            }
        }

        private void SetupFileSystemWatcher() {
            
            _dirWatcher.EnableRaisingEvents = false;

            _dirWatcher.Filter = LogPattern;
            _dirWatcher.Created += OnDirectoryChanged;
            _dirWatcher.Deleted += OnDirectoryChanged;
            _dirWatcher.Renamed += OnDirectortyRenamed;
        }

        private void TearDownFileSystemWatcher() {

            _dirWatcher.EnableRaisingEvents = false;
            _dirWatcher.Dispose();
        }

        private void OnDirectortyRenamed(object sender, RenamedEventArgs e) {
            _uiDispatcher.BeginInvoke(new Action(ReadListOfAvailableLogFiles), null);
        }

        private void OnDirectoryChanged(object sender, FileSystemEventArgs e) {
            _uiDispatcher.BeginInvoke(new Action(ReadListOfAvailableLogFiles), null);
        }

        private void ConfigureFileSystemWatcher() {

            _dirWatcher.EnableRaisingEvents = false;
            _dirWatcher.Path = CurrentLogsPath;
            _dirWatcher.EnableRaisingEvents = true;
        }

        private void DetermineLogDirectory() {

            RegistryKey wseKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Shared Tools\Web Server Extensions");

            if (wseKey != null) {

                const int MinVersion = 12;
                const int MaxVersion = 25;

                List<string> wseSubKeyNames = new List<string>(wseKey.GetSubKeyNames());

                // start with MaxVersion as I saw an installation which had a mostly empty 14.0, but a full 15.0 registry key
                for (int version = MaxVersion; version >= MinVersion; version--) {

                    string versionName = String.Format("{0}.0", version);
                    if (wseSubKeyNames.Contains(versionName)) {


                        RegistryKey wssKey = wseKey.OpenSubKey(versionName + @"\WSS");
                        if (wssKey != null) {

                            string path = (string) wssKey.GetValue("LogDir");
                            if (! String.IsNullOrEmpty(path)) {

                                _sharePointLogsPath = path;
                                // entry may contain references to environment variables
                                _sharePointLogsPath = Environment.ExpandEnvironmentVariables(_sharePointLogsPath);
                                break;
                            }
                        }
                    }
                }
            }
        }

        private void ComputeClosestDateExplanation() {

            DateTime userDate;
            if (DateTime.TryParse(SelectedLogDate, out userDate)) {

                UserSelectedDate = userDate;

                DateTime maxDateTimeBeforeSelection = DateTime.MinValue;

                foreach (LogFileInfo lfi in LogFiles) {

                    if (lfi.DateTime <= UserSelectedDate) {

                        if (lfi.DateTime > maxDateTimeBeforeSelection) {

                            maxDateTimeBeforeSelection = lfi.DateTime;
                            _selectedLogFileByDateTime = lfi;
                        }
                    }
                }

                if (maxDateTimeBeforeSelection == DateTime.MinValue) {

                    _closestLogFileExplanation = "Could not find a log file containing this time";
                    _selectedLogFileByDateTime = null;
                } else {

                    TimeSpan diff = UserSelectedDate - maxDateTimeBeforeSelection;
                    _closestLogFileExplanation =
                            String.Format("Found a log file starting {0} minute{1} before this time", diff.TotalMinutes,
                                          diff.TotalMinutes > 1 ? "s" : String.Empty);
                    // remember for next use of the dialog
                    s_lastSelectedLogDate = UserSelectedDate;
                }
            } else {
                _selectedLogFileByDateTime = null;
                _closestLogFileExplanation = String.Format("Invalid date/time in {0} format", CultureInfo.CurrentCulture.EnglishName);
            }

            OnPropertyChanged("ClosestLogFileExplanation");
        }

        void IDropTarget.HandleDragOver(DragEventArgs dea) {

            bool acceptable = dea.Data.GetDataPresent(DataFormats.FileDrop);

            if (acceptable) {

                string[] data = (string[]) dea.Data.GetData(DataFormats.FileDrop);

                if (data.Length > 1) {

                    // can only be files for us
                    foreach (string path in data) {

                        FileInfo fi = new FileInfo(path);
                        if (!fi.Exists) {
                            acceptable = false;
                            break;
                        }
                    }
                } else {
                    // can be a dir or a file
                    string path = data[0];
                    DirectoryInfo di = new DirectoryInfo(path);
                    if (! di.Exists) {

                        FileInfo fi = new FileInfo(path);
                        if (!fi.Exists) {
                            acceptable = false;
                        }
                    }
                }
            }

            dea.Handled = true;
            dea.Effects = acceptable ? DragDropEffects.Copy : DragDropEffects.None;
        }

        void IDropTarget.HandleDrop(DragEventArgs dea) {
            
            string[] data = (string[]) dea.Data.GetData(DataFormats.FileDrop);

            if (data.Length > 1) {

                // just files
                    
                SelectedLogFiles.Clear();

                foreach (string path in data) {

                    FileInfo fi = new FileInfo(path);
                    SelectedLogFiles.Add(new LogFileInfo(fi));
                }

                _shouldRecomputeSelectedFiles = false;
                OnOpen();
            } else {

                // can be a dir or a file
                string path = data[0];
                DirectoryInfo di = new DirectoryInfo(path);
                if (di.Exists) {

                    CurrentLogsPath = path;
                } else {

                    ExplicitLogFilePath = path;
                    OnOpen();
                }
            }

            dea.Handled = true;
        }

        private void DebugLog(string text, params object[] parameters) {
            App.DebugLog(text, parameters);
        }

    }

    public class LogFileInfo {

        private const string UnknownValue = "N/A";

        /// <summary>
        /// Host found in file name
        /// </summary>
        public string HostName { get; set; }
        /// <summary>
        /// Day found in file name
        /// </summary>
        public string DisplayedDay { get; set; }
        /// <summary>
        /// Start time found in file name
        /// </summary>
        public string DisplayedTime { get; set; }
        /// <summary>
        /// The involved file
        /// </summary>
        public FileInfo OriginalFile { get; set; }
        /// <summary>
        /// The date and file in the file name
        /// </summary>
        public DateTime DateTime { get; set; }

        /// <summary>
        /// The file size, in a simplified display
        /// </summary>
        public string FileSize { get; set; }

        /// <summary>
        /// Set to true when annotations have been read from disk, false otherwise
        /// </summary>
        public bool AreAnnotationsDeserialized { get; set; }
        public bool IsLikelyASharePointLogFile {
            get {
                return DisplayedDay != UnknownValue &&
                       DisplayedTime != UnknownValue &&
                       HostName != UnknownValue;
            }
        }

        public LogFileInfo(FileInfo fi) {

            string[] fileParts = fi.Name.Split('-');
            int partsCount = fileParts.Length;

            // pessimistic: set the values to 'unknown'
            DisplayedTime = UnknownValue;
            DisplayedDay = UnknownValue;
            HostName = UnknownValue;

            OriginalFile = fi;

            // some file names have HHMM + a letter if more logs happen in a single second than can fit in a log file
            string time = fileParts[partsCount - 1].Substring(0, 4);

            int min = 0, hour = 0;

            if (time.Length >= 4) {
                string sHour = time.Substring(0, 2);
                string sMin = time.Substring(2,2);
                DisplayedTime = sHour + ":" + sMin;
                Int32.TryParse(sHour, out hour);
                Int32.TryParse(sMin, out min);
            }

            if (partsCount > 1) {

                string date = fileParts[partsCount - 2];
                if (date.Length == 8) {
                    // TODO: do something about user preferences for date format
                    string sDay = date.Substring(6);
                    string sMonth = date.Substring(4, 2);
                    string sYear = date.Substring(0, 4);
                    int year;
                    Int32.TryParse(sYear, out year);
                    int month;
                    Int32.TryParse(sMonth, out month);
                    int day;
                    Int32.TryParse(sDay, out day);
                    DateTime = new DateTime(year, month, day, hour, min, 0);
                    DisplayedDay = DateTime.ToShortDateString();
                }
            }

            if (partsCount > 2) {

                HostName = String.Join("-", fileParts, 0, partsCount - 2);
            }

            // it happened to me that SharePoint deleted a log file between the time the directory contents were read and the time this code executed => FileNotFoundException
            long size;
            try {
                size = fi.Length;
            } catch (FileNotFoundException) {
                OriginalFile = null;
                return;
            }

            if (size < 1024) {
                FileSize = "<1K";
            } else if (size < 1024 * 1024) {

                FileSize = (size / 1024.0).ToString("0.0", CultureInfo.InvariantCulture) + " K";
            } else if (size < 1024 * 1024 * 1024) {

                FileSize = (size / (1024 * 1024.0)).ToString("0.0", CultureInfo.InvariantCulture) + " M";
            } else {
                FileSize = (size / (1024 * 1024 * 1024.0)).ToString("0.0", CultureInfo.InvariantCulture) + " G";
            }
        }

        public override bool Equals(object obj) {

            LogFileInfo other = obj as LogFileInfo;
            if (other == null) {
                return false;
            }

            return OriginalFile.FullName.Equals(other.OriginalFile.FullName);
        }

        public override int GetHashCode() {
            return OriginalFile.FullName.GetHashCode();
        }
    }
}
